import {
  UserState,
  ProviderType,
  CommlibErrorType,
  DeviceState,
  ChannelType
} from './common/commlib.enums';
import { Subject, from } from 'rxjs';
import { Util } from './common/util';
import { AWSDevice } from './providers/aws-connect/aws-device';
import { ConfigurationService } from './configuration/configuration.service';
import {
  ICommunicationLibrary,
  IDevice,
  IDeviceConfiguration
} from './common/commlib.interfaces';
import { SendBirdDevice } from './providers/sendbird/sendbird-device';

export class CommunicationLibrary implements ICommunicationLibrary {
  private static instance: CommunicationLibrary;
  private util: Util;
  private devices: IDevice[] = [];
  private userState: UserState = UserState.UNKNOWN;
  private configuration: ConfigurationService;
  public onError$ = new Subject<any>();
  public onNewEvent$ = new Subject<any>();

  private constructor() {
    this.configuration = new ConfigurationService();
    const aDeviceConfigurations = this.configuration.getConfigurations();
    for (const deviceCfg of aDeviceConfigurations) {
      if (deviceCfg.isActive) {
        if (deviceCfg.type === ProviderType.PREDEFINED) {
          switch (deviceCfg.key) {
            case 'SAP-AWS-CONNECT':
              try {
                const oProvider: IDevice = AWSDevice.init(
                  deviceCfg.parameters,
                  this
                );
                this.devices.push(oProvider);
              } catch (e) {
                this.onError$.next({
                  type: CommlibErrorType.INITIAL_FAILED,
                  payload: e
                });
              }
              break;

            case 'SENDBIRD':
              try {
                const oProvider: IDevice = SendBirdDevice.init(
                  deviceCfg.parameters,
                  this
                );
                this.devices.push(oProvider);
              } catch (e) {
                this.onError$.next({
                  type: CommlibErrorType.INITIAL_FAILED,
                  payload: e
                });
              }
              break;

            default:
              console.error('unknow predefined provider is found');
          }
        } else {
          this.dynamicLoadDevices(deviceCfg);
        }
      }
    }
  }

  public static getCommlib(): CommunicationLibrary {
    if (!this.instance) {
      this.instance = new CommunicationLibrary();
    }
    return this.instance;
  }

  public getDevices(): IDevice[] {
    return this.devices;
  }

  public getDeviceById(id: string): IDevice {
    return this.devices.filter(item => id === item.getId())[0];
  }

  public getDeviceByType(channelType: ChannelType): IDevice {
    return this.devices.filter(item => channelType === item.getType())[0];
  }

  public getUserState(): UserState {
    return this.userState;
  }

  public setUserState(newState: UserState): void {
    switch (newState) {
      case UserState.AVAILABLE:
        for (const oDevice of this.devices) {
          if (oDevice.getState() !== DeviceState.CONNECTED) {
            oDevice.register();
          }
        }
        break;

      case UserState.UNAVAILABLE:
        // Call unregister even the device state is not connected;
        for (const oDevice of this.devices) {
          oDevice.unregister();
        }
        break;

      case UserState.UNKNOWN:
      case UserState.BUSY:
        break;

      default:
        //
        break;
    }
  }

  private dynamicLoadDevices(deviceCfg: IDeviceConfiguration) {
    if (
      !deviceCfg ||
      !deviceCfg.location ||
      !deviceCfg.location.path ||
      !deviceCfg.location.className
    ) {
      return;
    }
    this.importModule(deviceCfg.location.path)
      .then(device => {
        if (!device || typeof device.init !== 'function') {
          return;
        }
        this.devices.push(device.init(deviceCfg, this));
      })
      .catch(e => {
        console.error(`Dynamic loading device with error: ${e}`);
      });
  }

  private importModule(url: string): Promise<any> {
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      const tempGlobal =
        '__tempDeviceModule' +
        Math.random()
          .toString(32)
          .substring(2);
      script.type = 'module';
      script.textContent = `import * as deviceModule from "${url}"; window.${tempGlobal} = deviceModule;`;

      script.onload = () => {
        resolve(window[tempGlobal]);
        // delete window[tempGlobal];
        // script.remove();
      };

      script.onerror = () => {
        reject(new Error('Failed to load module script with URL ' + url));
        // delete window[tempGlobal];
        // script.remove();
      };

      document.documentElement.appendChild(script);
    });
  }
}
